﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Common
{

    public static class NamedPipeLib
    {
        public const uint PIPE_ACCESS_DUPLEX = 0x00000003;

        public const uint PIPE_READMODE_MESSAGE = 0x00000002;

        public const uint PIPE_TYPE_MESSAGE = 0x00000004;

        public const uint PIPE_WAIT = 0x00000000;

        public const uint PIPE_UNLIMITED_INSTANCES = 255;

        public const int INVALID_HANDLE_VALUE = -1;

        public const ulong ERROR_PIPE_CONNECTED = 535;

        public const uint GENERIC_WRITE = (0x40000000);

        public const uint GENERIC_READ = (0x80000000);

        public const uint OPEN_EXISTING = 3;

        public const ulong ERROR_PIPE_BUSY = 231;

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr CreateFile(
            String lpFileName,						  // file name
            uint dwDesiredAccess,					  // access mode
            uint dwShareMode,								// share mode
            SecurityAttributes attr,				// SD
            uint dwCreationDisposition,			// how to create
            uint dwFlagsAndAttributes,			// file attributes
            uint hTemplateFile);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr CreateNamedPipe(
            String lpName,									// pipe name
            uint dwOpenMode,								// pipe open mode
            uint dwPipeMode,								// pipe-specific modes
            uint nMaxInstances,							// maximum number of instances
            uint nOutBufferSize,						// output buffer size
            uint nInBufferSize,							// input buffer size
            uint nDefaultTimeOut,						// time-out interval
            IntPtr pipeSecurityDescriptor		// SD
            );


        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool ConnectNamedPipe(
            IntPtr hHandle,									// handle to named pipe
            Overlapped lpOverlapped					// overlapped structure
            );


        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool ReadFile(
            IntPtr hHandle,											// handle to file
            byte[] lpBuffer,								// data buffer
            uint nNumberOfBytesToRead,			// number of bytes to read
            byte[] lpNumberOfBytesRead,			// number of bytes read
            uint lpOverlapped								// overlapped buffer
            );


        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool WriteFile(
            IntPtr hHandle,											// handle to file
            byte[] lpBuffer,							  // data buffer
            uint nNumberOfBytesToWrite,			// number of bytes to write
            byte[] lpNumberOfBytesWritten,	// number of bytes written
            uint lpOverlapped								// overlapped buffer
            );


        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool FlushFileBuffers(
            IntPtr hHandle);


        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool DisconnectNamedPipe(
            IntPtr hHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetNamedPipeHandleState(
            IntPtr hHandle,
            ref uint mode,
            IntPtr cc,
            IntPtr cd);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool WaitNamedPipe(
            String name,
            int timeout);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool CloseHandle(
            IntPtr hHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint GetLastError();


        public static void SendMsg(string msg, bool sendToPMYBrowser = true)
        {
            string pipeName = @"\\.\pipe\PMYMain";
            if (sendToPMYBrowser)
                pipeName = @"\\.\pipe\PMYBrowser";

            byte[] msgBuffer = Encoding.UTF8.GetBytes(msg);

            IntPtr fileHandle;
            while (true)
            {
                fileHandle = NamedPipeLib.CreateFile(pipeName,
                    NamedPipeLib.GENERIC_READ | NamedPipeLib.GENERIC_WRITE,
                    0, null, NamedPipeLib.OPEN_EXISTING, 0, 0);
                if (fileHandle.ToInt32() != NamedPipeLib.INVALID_HANDLE_VALUE)
                    break;
                if (NamedPipeLib.GetLastError() != NamedPipeLib.ERROR_PIPE_BUSY)
                {
                    //MessageBox.Show("不能打开管道");
                    return;
                }
                if (!NamedPipeLib.WaitNamedPipe(pipeName, 20000))
                {
                    //Console.WriteLine("不能打开管道");
                    return;
                }
            }
            uint pipeMode = NamedPipeLib.PIPE_READMODE_MESSAGE;
            bool success = NamedPipeLib.SetNamedPipeHandleState(fileHandle, ref pipeMode, IntPtr.Zero, IntPtr.Zero);

            if (!success)
            {
                //Console.WriteLine("设置管道状态失败");
                return;
            }

            uint len = (uint)msgBuffer.Length;
            byte[] numReadWritten = new byte[4];
            success = NamedPipeLib.WriteFile(fileHandle, BitConverter.GetBytes(len), 4, numReadWritten, 0);

            if (!success)
            {
                //Console.WriteLine("写入数据失败");
                return;
            }
            else
            {
                NamedPipeLib.WriteFile(fileHandle, msgBuffer, len, numReadWritten, 0);
            }
            NamedPipeLib.CloseHandle(fileHandle);
        }

        public static void SendMsg(ShareMessage msg, bool sendToPMYBrowser = true)
        {
            string pipeName = @"\\.\pipe\PMYMain";
            if (sendToPMYBrowser)
                pipeName = @"\\.\pipe\PMYBrowser";

            byte[] msgBuffer = BytesConvert.ObjectToBytes<ShareMessage>(msg);

            IntPtr fileHandle;
            while (true)
            {
                fileHandle = NamedPipeLib.CreateFile(pipeName,
                    NamedPipeLib.GENERIC_READ | NamedPipeLib.GENERIC_WRITE,
                    0, null, NamedPipeLib.OPEN_EXISTING, 0, 0);
                if (fileHandle.ToInt32() != NamedPipeLib.INVALID_HANDLE_VALUE)
                    break;
                if (NamedPipeLib.GetLastError() != NamedPipeLib.ERROR_PIPE_BUSY)
                {
                    //MessageBox.Show("不能打开管道");
                    return;
                }
                if (!NamedPipeLib.WaitNamedPipe(pipeName, 20000))
                {
                    //Console.WriteLine("不能打开管道");
                    return;
                }
            }
            uint pipeMode = NamedPipeLib.PIPE_READMODE_MESSAGE;
            bool success = NamedPipeLib.SetNamedPipeHandleState(fileHandle, ref pipeMode, IntPtr.Zero, IntPtr.Zero);

            if (!success)
            {
                //Console.WriteLine("设置管道状态失败");
                return;
            }

            uint len = (uint)msgBuffer.Length;
            byte[] numReadWritten = new byte[4];
            success = NamedPipeLib.WriteFile(fileHandle, BitConverter.GetBytes(len), 4, numReadWritten, 0);

            if (!success)
            {
                //Console.WriteLine("写入数据失败");
                return;
            }
            else
            {
                NamedPipeLib.WriteFile(fileHandle, msgBuffer, len, numReadWritten, 0);
            }
            NamedPipeLib.CloseHandle(fileHandle);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    public class Overlapped
    {
    }

    [StructLayout(LayoutKind.Sequential)]
    public class SecurityAttributes
    {
    }
}
